﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
// Copyright (C) 2021 kingerrobot
// 版权所有。 
//
// 文件名：BWcsAsrsNetInput
// 文件功能描述：
//
// 创建者：名字 (zzl)
// 时间：2021/12/7 16:43:41
//
// 修改人：
// 时间：
// 修改说明：
// 版本：V1.0.0
//----------------------------------------------------------------*/
#endregion


using System.Collections.Generic;

namespace KingerRobot.MexicoSailunMDC.SpiService;

internal class BWcsAsrsNetInput : IWcsAsrsNetInput
{
    private readonly static ITask _Task = new BTask();
    private readonly static ITaskCmd _TaskCmd = new BTaskCmd();
    private readonly static IRcsLocation _RcsLocation = new BRcsLocation();
    private readonly static IWcsEquipLocHold _WcsEquipLocHold = new BWcsEquipLocHold();
    private readonly static IWcsAsrsLocHalt _WcsAsrsLocHalt = new BWcsAsrsLocHalt();
    private readonly static IRcsEquipCrn _RcsEquipCrn = new BRcsEquipCrn();
    private readonly static IWmsSetting _WmsSetting = new BWmsSetting();
    private readonly static IWcsAsrsCrnInput _WcsAsrsCrnInput = new BWcsAsrsCrnInput();
    private readonly static IWcsWarehouseCrnMaterial _WcsWarehouseCrnMaterial = new BWcsWarehouseCrnMaterial();
    private readonly static IWcsEquipWarehouseDistance _WcsEquipWarehouseDistance = new BWcsEquipWarehouseDistance();
    private readonly static IMesCuringRequestMaterial _MesCuringRequestMaterial = new BMesCuringRequestMaterial();
    private readonly static IRcsEquip _RcsEquip = new BRcsEquip();
    private readonly static IWcsEquipLoc _WcsEquipLoc = new BWcsEquipLoc();
    private readonly static IProdMaterial _ProdMaterial = new BProdMaterial();
    private readonly static IWcsScannerWarehouseDistance _WcsScannerWarehouseDistance = new BWcsScannerWarehouseDistance();

    public long GetAsrsNetInputByStartLocTargetLoc(long startLoc, long targetLoc)
    {
        var db = Db.Default;
        var result = db.WcsAsrsNetInputs
            .Where(t => t.UseFlag == 1 && t.StartLoc == startLoc && (t.TargetLoc == targetLoc || t.StartCacheLoc == targetLoc)).FirstOrDefault();
        var locId = result?.StartCacheLoc ?? 0;

        if (locId != 0)
        {
            var equipId = _WcsEquipLoc.GetEquipIdByLocId(locId);
            var isEnable = _RcsEquip.IsEnableEquipById(equipId);
            if (isEnable)
            {
                return locId;
            }
        }
        return 0;
    }
    public long GetAsrsNetInputByTargetLoc(long targetLoc)
    {
        var db = Db.Default;
        var result = db.WcsAsrsNetInputs
            .Where(t => t.TargetLoc == targetLoc).FirstOrDefault();
        return result?.Id ?? 0;
    }
    public long GetAsrsNetInputByStartAndTargetLoc(long startLoc, long targetLoc)
    {
        var db = Db.Default;
        var result = db.WcsAsrsNetInputs
            .Where(t => t.TargetLoc == targetLoc && t.StartLoc == startLoc).FirstOrDefault();
        return result?.Id ?? 0;
    }

    public List<POWcsAsrsNetInput> GetAsrsNetInputByStartLoc(long startLoc)
    {
        var db = Db.Default;
        var result = db.WcsAsrsNetInputs
            .Where(t => t.UseFlag == 1 && t.StartLoc == startLoc).ToList();
        return result;
    }

    /// <summary>
    /// 入库前目标 选择货位
    /// </summary>
    /// <param name="curingCrns">硫化机请料堆垛机</param>
    /// <param name="scannerLocId"></param>
    /// <param name="materialId"></param>
    /// <param name="height"></param>
    /// <param name="outSide"></param>
    /// <param name="materialCode"></param>
    /// <param name="oddNoMaterialCrnIds">奇数任务堆垛机</param>
    /// <returns></returns>
    public long GetAsrsNetInputByStartLocAndNotInCrnIds(long scannerLocId, long materialId,
        List<long> oddNoMaterialCrnIds,
       string materialCode)
    {
        // 所有空货位
        var taskNogood = Task.Run(() => { return _RcsLocation.GetNoGoodsLocations(); });
        //路线
        var taskEquipWarehouseDistance = Task.Run(() => { return _WcsEquipWarehouseDistance.GetEquipWarehouseDistance(); });
        //堆垛机持有货位
        var taskEquipLocHold = Task.Run(() => { return _WcsEquipLocHold.GetEquipLocHolds(); });
        // 所有货叉信息(IsEnable : True 可用；False 不可用)
        var crnAllForkInfos = _RcsEquipCrn.GetCrnFork();
        var usableCrnForkInfos = crnAllForkInfos.Where(t => t.IsEnable == true).ToList(); // 可用

        if (usableCrnForkInfos.Count == 0)
        {
            return -1;
        }
        var abnormalCrnForkInfos = crnAllForkInfos.Where(t => t.IsEnable == false).ToList(); // 异常
        var warehourseIds = usableCrnForkInfos.Select(t => t.WarehourseId).Distinct().ToList();
        //放胎位缓存数量>0
        var inputUserCrnIdAll = _WcsAsrsCrnInput.GetWcsAsrsCrnInputs();
        var inputUserCrnIds = inputUserCrnIdAll.Where(t => t.TaskCount > 0).ToList();
        if (inputUserCrnIds.Count == 0)
        {
            return -2;
        }
        //WriteLog($"入库进入选货位108", true);
        var inputCrnIds = inputUserCrnIds.Select(t => t.CrnId).ToList();
        //WriteLog($"入库进入选货位110", true);
        // 偶数任务 堆垛机货叉
        var evenForkCrnIds = usableCrnForkInfos
            .Where(t => !oddNoMaterialCrnIds.Contains(t.CrnId.GetValueOrDefault())
            && inputCrnIds.Contains(t.CrnId.GetValueOrDefault()))
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        //WriteLog($"入库进入选货位118", true);
        // 奇数任务 堆垛机货叉
        var oddForkCrnIds = usableCrnForkInfos
            .Where(t => oddNoMaterialCrnIds.Contains(t.CrnId.GetValueOrDefault())
            && inputCrnIds.Contains(t.CrnId.GetValueOrDefault()))
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        List<CrnSort> crnSortList = new();
        crnSortList.AddRange(evenForkCrnIds.Select(t => new CrnSort { CrnId = t.GetValueOrDefault(), Sort = 1 }));
        crnSortList.AddRange(oddForkCrnIds.Select(t => new CrnSort { CrnId = t.GetValueOrDefault(), Sort = 2 }));
        // 有效入库路线
        var crnIds = crnSortList.Select(t => t.CrnId).ToList();
        var wcsAsrsNetInputs = GetWcsAsrsNetInput(scannerLocId, crnIds);
        var locations = taskNogood.Result;
        var locHold = taskEquipLocHold.Result;
        // 堆垛机管理空货位
        var equipLocHold = _WcsEquipLocHold.GetNoGoodsEquipLocHoldsByCrn(usableCrnForkInfos, abnormalCrnForkInfos, locations, locHold);
        if (equipLocHold.Count == 0)
        {
            return -3;
        }


        //// 去掉不满足大小胎 如果是NONE物料 直接进小胎
        //GetLocTireType(ref equipLocHold, locations, materialCode);

        //if (equipLocHold.Count == 0)
        //{
        //    return -6;
        //}
        // 通过空货位位查找入库路线
        GetCrnStartLocNoGoods(ref wcsAsrsNetInputs, equipLocHold);

        //此处货位数-2
        if (wcsAsrsNetInputs.Count <= 0)
        {
            return -7; // 无空货位路线，等待入库
        }
        // 通过任务数未饱和查找入库路线（优先选择任务数不满，其次选择任务数超限）
        var crnTaskCounts = GetStartLocTasksV2(ref wcsAsrsNetInputs, inputUserCrnIds);

        if (wcsAsrsNetInputs.Count == 0)
        {
            // 任务满临时添加，等待入库
            return -8;
        }

        var crns = _RcsEquipCrn.GetRcsEquipCrn();

        // 根据物料获取硫化机信息
        var curingMaterials = _MesCuringRequestMaterial.GetCuringRequestMaterialByMaterialId(materialId);

        // 先通过物料查找匹配的堆垛机
        var warehouseCrnMaterials = _WcsWarehouseCrnMaterial.GetWarehouseCrnMaterialByMaterialId(crns, materialId);

        // 在途物料汇总
        var wayCrnMaterials = _Task.GetTaskCrnGroupMaterial(crns, inputUserCrnIdAll);

        wayCrnMaterials.ForEach(item =>
        {
            var crnMaterialBean = warehouseCrnMaterials.Where(t => t.CrnId == item.CrnId).FirstOrDefault();
            if (crnMaterialBean is not null)
            {
                item.Count += crnMaterialBean.Count;
            }
            else
            {
                warehouseCrnMaterials.Add(new()
                {
                    CrnId = item.CrnId,
                    WarehouseId = item.WarehouseId,
                    MaterialId = item.MaterialId,
                    Count = item.Count,
                });
            }
        });

        var areaSorts = new List<AreaSort>();
        var areaIds = new List<long>();

        if (materialCode != ConstFields.NONE && curingMaterials.Any())
        {
            //查找库区的物料数量
            var warehouseCrnMaterialGroup = warehouseCrnMaterials.
                 GroupBy(t => new { t.WarehouseId, t.MaterialId }).Select(t => new
                 {
                     WarehouseId = t.Key.WarehouseId,
                     MaterialId = t.Key.MaterialId,
                     MaterialCount = t.Sum(t => t.Count)
                 });

            //跟库区左连接 如果无此物料 数量为0
            var warehouseMaterialCount = warehourseIds.GroupJoin(warehouseCrnMaterialGroup, f => f, s => s.WarehouseId,
                 (f, s) => new
                 {
                     WarehouseId = f,
                     material = s
                 }).SelectMany(f => f.material.DefaultIfEmpty(), (f, s) => new
                 {
                     WarehouseId = f.WarehouseId,
                     MaterialCount = s?.MaterialCount ?? 0,
                 }).ToList();

            //根据获取所有硫化机堆垛机(库区)的距离
            var equipWarehouseDistance = taskEquipWarehouseDistance.Result;//_WcsEquipWarehouseDistance.GetEquipWarehouseDistance();

            // 库区按距离跟物料均衡排序 找到最优区域
            areaIds = equipWarehouseDistance.Join(curingMaterials, f => f.CuringLocId, s => s.CuringLocId, (f, s) => new
            {
                distance = f,
                curings = s
            }).Join(warehouseMaterialCount, f => f.distance.WarehouseId, s => s.WarehouseId, (f, s) => new
            {
                distance = f.distance,
                MaterialCount = s.MaterialCount,
            })
            .OrderBy(t => t.distance.Distance)
            .ThenBy(t => t.MaterialCount)
            .Select(t => t.distance.AreaId.GetValueOrDefault())
            .Distinct()
            .ToList();

        }




        if (areaIds.Count == 0)
        {
            //由近到远区域
            areaIds = wcsAsrsNetInputs.OrderBy(t => t.RelateScore).Select(t => t.AreaId.GetValueOrDefault()).Distinct().ToList();
        }

        // 增加排序
        areaSorts = areaIds.Select((item, t) => new AreaSort
        {
            AreaId = item,
            Sort = t,
        }).ToList();
        // 获取优先入库区域路线

        var areaCrnNetInputs = wcsAsrsNetInputs.Where(t => areaIds.Contains(t.AreaId.GetValueOrDefault())).ToList();

        if (areaCrnNetInputs.Count == 0)
        {
            return 0;
        }
        //入库区域关联 查找每个堆垛机的相关物料数量
        var areaMaterialList = areaCrnNetInputs.GroupJoin(warehouseCrnMaterials, f => f.CrnId, s => s.CrnId, (f, s) => new
        {
            AreaCrnNetInput = f,
            Material = s,
        }).SelectMany(f => f.Material.DefaultIfEmpty(), (f, s) => new
        {
            f.AreaCrnNetInput,
            MaterialCount = s?.Count,
        }).Join(crnSortList, f => f.AreaCrnNetInput.CrnId, s => s.CrnId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.MaterialCount,
            CrnSort = s,
        }).Join(crnTaskCounts, f => f.AreaCrnNetInput.CrnId, s => s.CrnId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.MaterialCount,
            f.CrnSort,
            CrnTask = s,
        }).Join(areaSorts, f => f.AreaCrnNetInput.AreaId, s => s.AreaId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.MaterialCount,
            f.CrnSort,
            f.CrnTask,
            AreaSort = s,
        })
        .OrderBy(t => t.AreaSort.Sort)  // 库区（由近到远）
        .ThenBy(t => t.CrnSort.Sort)    // 偶数
        .ThenBy(t => t.MaterialCount)   // 物料均衡数量少
        .ThenBy(t => t.CrnTask.Count)   // 任务数少
        .ThenByDescending(t => t.AreaCrnNetInput.RelateScore).ToList();// 堆垛机距离（由远到近）
        //.FirstOrDefault();


        var areaMaterial = areaMaterialList.FirstOrDefault();

        if (areaMaterial is null || areaMaterial.AreaCrnNetInput is null)
        {
            return 0;
        }
        var crn = areaMaterial.AreaCrnNetInput;
        return crn.TargetLoc.GetValueOrDefault();
    }



    public long GetAsrsNetInputByStartLocAndNotInCrnIdsV2(long scannerLocId, long materialId,
    List<long> oddNoMaterialCrnIds,
     string materialCode)
    {

        // 所有空货位
        var taskNogood = Task.Run(_RcsLocation.GetNoGoodsLocations);


        //堆垛机持有货位
        var taskEquipLocHold = Task.Run(_WcsEquipLocHold.GetEquipLocHolds);

        // 所有货叉信息(IsEnable : True 可用；False 不可用)
        var crnAllForkInfos = _RcsEquipCrn.GetCrnFork();
        var usableCrnForkInfos = crnAllForkInfos.Where(t => t.IsEnable == true).ToList(); // 可用

        if (usableCrnForkInfos.Count == 0)
        {
            return -1;
        }
        var material = _ProdMaterial.GetMaterialByCode(materialCode);
        var warehourseIds = usableCrnForkInfos.Select(t => t.WarehourseId).Distinct().ToList();
        var areaSorts = new List<AreaSort>();

        List<long> areaIds = _WcsScannerWarehouseDistance.GetScannerWarehouseDistanceByScannerLoc(scannerLocId)
            .OrderBy(t => t.Distance).Select(t => t.AreaId ?? 0).ToList();



        var abnormalCrnForkInfos = crnAllForkInfos.Where(t => t.IsEnable == false).ToList(); // 异常

        //放胎位缓存数量>0
        var inputUserCrnIdAll = _WcsAsrsCrnInput.GetWcsAsrsCrnInputs();

        var inputUserCrnIds = inputUserCrnIdAll.Where(t => t.TaskCount > 0).ToList();
        if (inputUserCrnIds.Count == 0)
        {
            return -2;
        }
        var inputCrnIds = inputUserCrnIds.Select(t => t.CrnId).ToList();

        // 偶数任务 堆垛机货叉
        var evenForkCrnIds = usableCrnForkInfos
            .Where(t => !oddNoMaterialCrnIds.Contains(t.CrnId.GetValueOrDefault())
            && inputCrnIds.Contains(t.CrnId.GetValueOrDefault()))
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        // 奇数任务 堆垛机货叉
        var oddForkCrnIds = usableCrnForkInfos
            .Where(t => oddNoMaterialCrnIds.Contains(t.CrnId.GetValueOrDefault())
            && inputCrnIds.Contains(t.CrnId.GetValueOrDefault()))
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        List<CrnSort> crnSortList =
        [
            .. evenForkCrnIds.Select(t => new CrnSort { CrnId = t.GetValueOrDefault(), Sort = 1 }),
            .. oddForkCrnIds.Select(t => new CrnSort { CrnId = t.GetValueOrDefault(), Sort = 2 }),
        ];
        // 有效入库路线
        var crnIds = crnSortList.Select(t => t.CrnId).ToList();
        var wcsAsrsNetInputs = GetWcsAsrsNetInput(scannerLocId, crnIds);
        var locations = taskNogood.Result;
        var locHold = taskEquipLocHold.Result;
        // 堆垛机管理空货位
        var equipLocHold = _WcsEquipLocHold.GetNoGoodsEquipLocHoldsByCrn(usableCrnForkInfos, abnormalCrnForkInfos, locations, locHold);

        //var equipLocHold = _WcsEquipLocHold.GetNoGoodsEquipLocHoldsByCrnV2(usableCrnForkInfos, abnormalCrnForkInfos, locations, locHold);
        if (equipLocHold.Count == 0)
        {
            return -3;
        }

        // 通过空货位位查找入库路线
        GetCrnStartLocNoGoods(ref wcsAsrsNetInputs, equipLocHold);
        //此处货位数-2
        if (wcsAsrsNetInputs.Count <= 0)
        {
            return -7; // 无空货位路线，等待入库
        }
        // 通过任务数未饱和查找入库路线（优先选择任务数不满，其次选择任务数超限）
        var crnTaskCounts = GetStartLocTasksV2(ref wcsAsrsNetInputs, inputUserCrnIds);
        if (wcsAsrsNetInputs.Count == 0)
        {
            // 任务满临时添加，等待入库
            return -8;
        }
        // 增加排序
        areaSorts = areaIds.Select((item, t) => new AreaSort
        {
            AreaId = item,
            Sort = t,
        }).ToList();
        // 获取优先入库区域路线
        var areaCrnNetInputs = wcsAsrsNetInputs.Where(t => areaIds.Contains(t.AreaId.GetValueOrDefault())).ToList();
        if (areaCrnNetInputs.Count == 0)
        {
            return 0;
        }

        var areaMaterialList = areaCrnNetInputs.Join(crnSortList, f => f.CrnId, s => s.CrnId, (f, s) => new
        {
            AreaCrnNetInput = f,
            CrnSort = s,
        }).Join(crnTaskCounts, f => f.AreaCrnNetInput.CrnId, s => s.CrnId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.CrnSort,
            CrnTask = s,
        }).Join(areaSorts, f => f.AreaCrnNetInput.AreaId, s => s.AreaId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.CrnSort,
            f.CrnTask,
            AreaSort = s,
        })
        .OrderBy(t => t.AreaSort.Sort)  // 库区（由近到远）
        .ThenBy(t => t.CrnSort.Sort)    // 偶数
        .ThenBy(t => t.CrnTask.Count)   // 任务数少
        .ThenByDescending(t => t.AreaCrnNetInput.RelateScore)// 堆垛机距离（由远到近）
        .ToList();

        var areaMaterial = areaMaterialList.FirstOrDefault();


        if (areaMaterial is null || areaMaterial.AreaCrnNetInput is null)
        {
            return 0;
        }
        var crn = areaMaterial.AreaCrnNetInput;

        return crn.TargetLoc.GetValueOrDefault();
    }


    public long GetPairAsrsNetInputByStartLocAndNotInCrnIds(long scannerLocId, string materialCode)
    {
        List<long> oddNoMaterialCrnIds = new();
        // 所有空货位
        var taskNogood = Task.Run(_RcsLocation.GetNoGoodsLocations);


        //堆垛机持有货位
        var taskEquipLocHold = Task.Run(_WcsEquipLocHold.GetEquipLocHolds);

        // 所有货叉信息(IsEnable : True 可用；False 不可用)
        var crnAllForkInfos = _RcsEquipCrn.GetCrnFork();
        var usableCrnForkInfos = crnAllForkInfos.Where(t => t.IsEnable == true).ToList(); // 可用

        if (usableCrnForkInfos.Count == 0)
        {
            return -1;
        }


        //放胎位缓存数量>0 此处修改为不按照堆垛机判断是否配对库位 按照rcs_location的是否配对库位进行判断
        // var inputUserCrnIdAll = _WcsAsrsCrnInput.GetWcsAsrsCrnInputs().Where(t => t.InputType == 1).ToList();
        var inputUserCrnIdAll = _WcsAsrsCrnInput.GetWcsAsrsCrnInputs();

        var inputUserCrnIds = inputUserCrnIdAll.Where(t => t.TaskCount > 0).ToList();
        if (inputUserCrnIds.Count == 0)
        {
            return -2;
        }
        var inputCrnIds = inputUserCrnIds.Select(t => t.CrnId).ToList();


        var material = _ProdMaterial.GetMaterialByCode(materialCode);


        var crns = _RcsEquipCrn.GetRcsEquipCrn();
        var materialId = material.Id ?? 0;
        var warehouseCrnMaterials = _WcsWarehouseCrnMaterial.GetWarehouseCrnMaterialByMaterialId(crns, materialId);

        // 在途物料汇总
        var wayCrnMaterials = _Task.GetTaskCrnGroupMaterial(crns, inputUserCrnIdAll);

        wayCrnMaterials.ForEach(item =>
        {
            var crnMaterialBean = warehouseCrnMaterials.Where(t => t.CrnId == item.CrnId).FirstOrDefault();
            if (crnMaterialBean is not null)
            {
                item.Count += crnMaterialBean.Count;
            }
            else
            {
                warehouseCrnMaterials.Add(new()
                {
                    CrnId = item.CrnId,
                    WarehouseId = item.WarehouseId,
                    MaterialId = item.MaterialId,
                    Count = item.Count,
                });
            }
        });




        var warehourseIds = usableCrnForkInfos.Select(t => t.WarehourseId).Distinct().ToList();
        var areaSorts = new List<AreaSort>();

        List<long> areaIds = _WcsScannerWarehouseDistance.GetScannerWarehouseDistanceByScannerLoc(scannerLocId)
            .OrderBy(t => t.Distance).Select(t => t.AreaId ?? 0).ToList();



        var abnormalCrnForkInfos = crnAllForkInfos.Where(t => t.IsEnable == false).ToList(); // 异常


        // 偶数任务 堆垛机货叉
        var evenForkCrnIds = usableCrnForkInfos
            .Where(t => !oddNoMaterialCrnIds.Contains(t.CrnId.GetValueOrDefault())
            && inputCrnIds.Contains(t.CrnId.GetValueOrDefault()))
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        // 奇数任务 堆垛机货叉
        var oddForkCrnIds = usableCrnForkInfos
            .Where(t => oddNoMaterialCrnIds.Contains(t.CrnId.GetValueOrDefault())
            && inputCrnIds.Contains(t.CrnId.GetValueOrDefault()))
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        List<CrnSort> crnSortList =
        [
            .. evenForkCrnIds.Select(t => new CrnSort { CrnId = t.GetValueOrDefault(), Sort = 1 }),
            .. oddForkCrnIds.Select(t => new CrnSort { CrnId = t.GetValueOrDefault(), Sort = 2 }),
        ];
        // 有效入库路线
        var crnIds = crnSortList.Select(t => t.CrnId).ToList();
        var wcsAsrsNetInputs = GetWcsAsrsNetInput(scannerLocId, crnIds);
        var locations = taskNogood.Result;
        locations = locations.Where(t => t.LocInputType == 1).ToList();//查询需要配对的库位
        //只筛选双空库位
        locations = _RcsLocation.GetDoubleRcsLocation(locations);
        var locHold = taskEquipLocHold.Result;
        // 堆垛机管理空货位
        var equipLocHold = _WcsEquipLocHold.GetNoGoodsEquipLocHoldsByCrn(usableCrnForkInfos, abnormalCrnForkInfos, locations, locHold);

        //var equipLocHold = _WcsEquipLocHold.GetNoGoodsEquipLocHoldsByCrnV2(usableCrnForkInfos, abnormalCrnForkInfos, locations, locHold);
        if (equipLocHold.Count == 0)
        {
            return -3;
        }

        // 通过空货位位查找入库路线
        GetCrnStartLocNoGoods(ref wcsAsrsNetInputs, equipLocHold);
        //此处货位数-2
        if (wcsAsrsNetInputs.Count <= 0)
        {
            return -7; // 无空货位路线，等待入库
        }
        // 通过任务数未饱和查找入库路线（优先选择任务数不满，其次选择任务数超限）
        var crnTaskCounts = GetStartLocTasksV2(ref wcsAsrsNetInputs, inputUserCrnIds);
        if (wcsAsrsNetInputs.Count == 0)
        {
            // 任务满临时添加，等待入库
            return -8;
        }
        // 增加排序
        areaSorts = areaIds.Select((item, t) => new AreaSort
        {
            AreaId = item,
            Sort = t,
        }).ToList();
        // 获取优先入库区域路线
        var areaCrnNetInputs = wcsAsrsNetInputs.Where(t => areaIds.Contains(t.AreaId.GetValueOrDefault())).ToList();
        if (areaCrnNetInputs.Count == 0)
        {
            return 0;
        }


        //入库区域关联 查找每个堆垛机的相关物料数量
        var areaMaterialList = areaCrnNetInputs.GroupJoin(warehouseCrnMaterials, f => f.CrnId, s => s.CrnId, (f, s) => new
        {
            AreaCrnNetInput = f,
            Material = s,
        }).SelectMany(f => f.Material.DefaultIfEmpty(), (f, s) => new
        {
            f.AreaCrnNetInput,
            MaterialCount = s?.Count,
        }).Join(crnSortList, f => f.AreaCrnNetInput.CrnId, s => s.CrnId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.MaterialCount,
            CrnSort = s,
        }).Join(crnTaskCounts, f => f.AreaCrnNetInput.CrnId, s => s.CrnId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.MaterialCount,
            f.CrnSort,
            CrnTask = s,
        }).Join(areaSorts, f => f.AreaCrnNetInput.AreaId, s => s.AreaId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.MaterialCount,
            f.CrnSort,
            f.CrnTask,
            AreaSort = s,
        })
        .OrderBy(t => t.AreaSort.Sort)  // 库区（由近到远）
        .ThenBy(t => t.CrnSort.Sort)    // 偶数
        .ThenBy(t => t.MaterialCount)   // 物料均衡数量少
        .ThenBy(t => t.CrnTask.Count)   // 任务数少
        .ThenByDescending(t => t.AreaCrnNetInput.RelateScore).ToList();// 堆垛机距离（由远到近）


        //var areaMaterialList = areaCrnNetInputs.Join(crnSortList, f => f.CrnId, s => s.CrnId, (f, s) => new
        //{
        //    AreaCrnNetInput = f,
        //    CrnSort = s,
        //}).Join(crnTaskCounts, f => f.AreaCrnNetInput.CrnId, s => s.CrnId, (f, s) => new
        //{
        //    f.AreaCrnNetInput,
        //    f.CrnSort,
        //    CrnTask = s,
        //}).Join(areaSorts, f => f.AreaCrnNetInput.AreaId, s => s.AreaId, (f, s) => new
        //{
        //    f.AreaCrnNetInput,
        //    f.CrnSort,
        //    f.CrnTask,
        //    AreaSort = s,
        //})
        //.OrderBy(t => t.AreaSort.Sort)  // 库区（由近到远）
        //.ThenBy(t => t.CrnSort.Sort)    // 偶数
        //.ThenBy(t => t.CrnTask.Count)   // 任务数少
        //.ThenByDescending(t => t.AreaCrnNetInput.RelateScore)// 堆垛机距离（由远到近）
        //.ToList();

        var areaMaterial = areaMaterialList.FirstOrDefault();


        if (areaMaterial is null || areaMaterial.AreaCrnNetInput is null)
        {
            return 0;
        }
        var crn = areaMaterial.AreaCrnNetInput;

        return crn.TargetLoc.GetValueOrDefault();
    }

    public long GetNoPairTargetLoc(long scannerLocId, string materialCode)
    {
        WriteLog($"物料不一致600", true);
        //判断堆垛机是否禁用 如果全部禁用 则下异常
        //判断缓存是否满 如果缓存满 则下发位等待
        //var inputUserCrnIdAll = _WcsAsrsCrnInput.GetWcsAsrsCrnInputs().Where(t => t.InputType == 2).ToList();
        var inputUserCrnIdAll = _WcsAsrsCrnInput.GetWcsAsrsCrnInputs();
        WriteLog($"物料不一致604", true);
        var crns = inputUserCrnIdAll.Select(t => t.CrnId).ToList();
        var crnCount = _RcsEquip.GetWorkEquipByIds(crns).Count();
        if (crnCount <= 0)
        {
            return -8888;
        }

        WriteLog($"物料不一致612", true);
        List<long> oddNoMaterialCrnIds = new();
        // 所有空货位
        var taskNogood = Task.Run(_RcsLocation.GetNoGoodsLocations);
        WriteLog($"物料不一致616", true);

        //堆垛机持有货位
        var taskEquipLocHold = Task.Run(_WcsEquipLocHold.GetEquipLocHolds);
        WriteLog($"物料不一致620", true);
        // 所有货叉信息(IsEnable : True 可用；False 不可用)
        var crnAllForkInfos = _RcsEquipCrn.GetCrnFork();
        var usableCrnForkInfos = crnAllForkInfos.Where(t => t.IsEnable == true).ToList(); // 可用
        WriteLog($"物料不一致624", true);
        if (usableCrnForkInfos.Count == 0)
        {
            return -1;
        }
        var material = _ProdMaterial.GetMaterialByCode(materialCode);
        var warehourseIds = usableCrnForkInfos.Select(t => t.WarehourseId).Distinct().ToList();
        var areaSorts = new List<AreaSort>();
        WriteLog($"物料不一致632", true);
        List<long> areaIds = _WcsScannerWarehouseDistance.GetScannerWarehouseDistanceByScannerLoc(scannerLocId)
            .OrderBy(t => t.Distance).Select(t => t.AreaId ?? 0).ToList();

        WriteLog($"物料不一致636", true);

        var abnormalCrnForkInfos = crnAllForkInfos.Where(t => t.IsEnable == false).ToList(); // 异常

        //放胎位缓存数量>0

        WriteLog($"物料不一致642", true);
        var inputUserCrnIds = inputUserCrnIdAll.Where(t => t.TaskCount > 0).ToList();
        if (inputUserCrnIds.Count == 0)
        {
            return -22;
        }
        var inputCrnIds = inputUserCrnIds.Select(t => t.CrnId).ToList();
        WriteLog($"物料不一致649", true);
        // 偶数任务 堆垛机货叉
        var evenForkCrnIds = usableCrnForkInfos
            .Where(t => !oddNoMaterialCrnIds.Contains(t.CrnId.GetValueOrDefault())
            && inputCrnIds.Contains(t.CrnId.GetValueOrDefault()))
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        // 奇数任务 堆垛机货叉
        var oddForkCrnIds = usableCrnForkInfos
            .Where(t => oddNoMaterialCrnIds.Contains(t.CrnId.GetValueOrDefault())
            && inputCrnIds.Contains(t.CrnId.GetValueOrDefault()))
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        List<CrnSort> crnSortList =
        [
            .. evenForkCrnIds.Select(t => new CrnSort { CrnId = t.GetValueOrDefault(), Sort = 1 }),
            .. oddForkCrnIds.Select(t => new CrnSort { CrnId = t.GetValueOrDefault(), Sort = 2 }),
        ];
        // 有效入库路线
        var crnIds = crnSortList.Select(t => t.CrnId).ToList();
        var wcsAsrsNetInputs = GetWcsAsrsNetInput(scannerLocId, crnIds);
        var locations = taskNogood.Result;
        locations = locations.Where(t => t.LocInputType == 2).ToList();//判断是否配对
        //只筛选双空库位
        locations = _RcsLocation.GetDoubleRcsLocation(locations);
        var locHold = taskEquipLocHold.Result;
        // 堆垛机管理空货位
        var equipLocHold = _WcsEquipLocHold.GetNoGoodsEquipLocHoldsByCrn(usableCrnForkInfos, abnormalCrnForkInfos, locations, locHold);
        WriteLog($"物料不一致678", true);
        //var equipLocHold = _WcsEquipLocHold.GetNoGoodsEquipLocHoldsByCrnV2(usableCrnForkInfos, abnormalCrnForkInfos, locations, locHold);
        if (equipLocHold.Count == 0)
        {
            return -23;
        }
        WriteLog($"物料不一致684", true);
        // 通过空货位位查找入库路线
        GetCrnStartLocNoGoods(ref wcsAsrsNetInputs, equipLocHold);
        //此处货位数-2
        if (wcsAsrsNetInputs.Count <= 0)
        {
            return -27; // 无空货位路线，等待入库
        }
        WriteLog($"物料不一致692", true);
        // 通过任务数未饱和查找入库路线（优先选择任务数不满，其次选择任务数超限）
        var crnTaskCounts = GetStartLocTasksV2(ref wcsAsrsNetInputs, inputUserCrnIds);
        if (wcsAsrsNetInputs.Count == 0)
        {
            // 任务满临时添加，等待入库
            return -28;
        }
        WriteLog($"物料不一致700", true);
        // 增加排序
        areaSorts = areaIds.Select((item, t) => new AreaSort
        {
            AreaId = item,
            Sort = t,
        }).ToList();
        WriteLog($"物料不一致707", true);
        // 获取优先入库区域路线
        var areaCrnNetInputs = wcsAsrsNetInputs.Where(t => areaIds.Contains(t.AreaId.GetValueOrDefault())).ToList();
        if (areaCrnNetInputs.Count == 0)
        {
            return 0;
        }
        WriteLog($"物料不一致714", true);
        var areaMaterialList = areaCrnNetInputs.Join(crnSortList, f => f.CrnId, s => s.CrnId, (f, s) => new
        {
            AreaCrnNetInput = f,
            CrnSort = s,
        }).Join(crnTaskCounts, f => f.AreaCrnNetInput.CrnId, s => s.CrnId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.CrnSort,
            CrnTask = s,
        }).Join(areaSorts, f => f.AreaCrnNetInput.AreaId, s => s.AreaId, (f, s) => new
        {
            f.AreaCrnNetInput,
            f.CrnSort,
            f.CrnTask,
            AreaSort = s,
        })
        .OrderBy(t => t.AreaSort.Sort)  // 库区（由近到远）
        .ThenBy(t => t.CrnSort.Sort)    // 偶数
        .ThenBy(t => t.CrnTask.Count)   // 任务数少
        .ThenByDescending(t => t.AreaCrnNetInput.RelateScore)// 堆垛机距离（由远到近）
        .ToList();
        WriteLog($"物料不一致736", true);
        var areaMaterial = areaMaterialList.FirstOrDefault();
        WriteLog($"物料不一致738", true);

        if (areaMaterial is null || areaMaterial.AreaCrnNetInput is null)
        {
            return 0;
        }
        WriteLog($"物料不一致744", true);
        var crn = areaMaterial.AreaCrnNetInput;
        WriteLog($"物料不一致746,{crn.TargetLoc}", true);
        return crn.TargetLoc.GetValueOrDefault();
    }


    /// <summary>
    /// 有效入库路线
    /// </summary>
    /// <param name="startLoc"></param>
    /// <returns></returns>
    private List<POWcsAsrsNetInput> GetWcsAsrsNetInput(long startLoc, List<long> crnIds)
    {
        var db = Db.Default;
        var inputs = db.WcsAsrsNetInputs
            .Where(t => t.StartLoc == startLoc && t.UseFlag == 1).ToList();

        //获取禁用的扫码头列表
        var errScannerIds = db.RcsEquips.Where(t => t.EquipType == 1 && t.EquipEnable == 0).Select(t => t.Id).ToList();
        //取开始位与缓存位都不禁用的入库路线
        inputs = inputs.Where(t => !errScannerIds.Contains(t.StartLoc) && !errScannerIds.Contains(t.StartCacheLoc)).ToList();

        var result = inputs.Join(crnIds, f => f.CrnId, s => s, (f, s) => f).OrderBy(t => t.RelateScore).ToList();
        return result;
    }

    /// <summary>
    /// 有效入库路线
    /// </summary>
    /// <param name="startLoc"></param>
    /// <returns></returns>
    private List<POWcsAsrsNetInput> GetWcsAsrsNetInputV2(long startLoc, List<CrnSort> crnSorts)
    {
        var db = Db.Default;
        var input = db.WcsAsrsNetInputs.Where(t => t.StartLoc == startLoc && t.UseFlag == 1).ToList();
        var result = crnSorts.GroupJoin(input,
            f => f.CrnId, s => s.CrnId, (f, s) => new { WcsAsrsNetInputs = s, Sort = f }).SelectMany(x => x.WcsAsrsNetInputs.DefaultIfEmpty(), (x, y) =>
            new { x.Sort, WcsAsrsNetInputs = y }
            ).Where(t => t.WcsAsrsNetInputs is not null).OrderBy(t => t.Sort.Sort).ThenBy(t => t.WcsAsrsNetInputs.RelateScore).Select(t => t.WcsAsrsNetInputs).ToList();
        return result;
    }


    /// <summary>
    /// 查看堆垛机放货位含 无货货位
    /// </summary>
    /// <returns></returns>
    private static void GetCrnStartLocNoGoods(ref List<POWcsAsrsNetInput> pOWcsAsrsNetInputs,
        List<POWcsEquipLocHold> equipLocHold)
    {
        var ids = equipLocHold.Select(t => t.EquipId).Distinct().ToList();
        pOWcsAsrsNetInputs = pOWcsAsrsNetInputs.Where(t => ids.Contains(t.CrnId)).ToList();
    }


    private void GetLocNoHalt(ref List<POWcsEquipLocHold> equipLocHold)
    {
        var asrsLocHalts = _WcsAsrsLocHalt.GetAsrsLocHalt();
        var locIds = asrsLocHalts.Select(t => t.LocId).ToList();
        equipLocHold = equipLocHold.Where(t => !locIds.Contains(t.LocId)).ToList();
    }

    private void GetLocHeight(ref List<POWcsEquipLocHold> equipLocHold, List<PORcsLocation> locations, decimal height)
    {
        equipLocHold = equipLocHold.Join(locations, f => f.LocId, s => s.Id, (f, s) => new
        {
            LocHold = f,
            Location = s,
        }).Where(t => t.Location.LocHeight >= height).Select(t => t.LocHold).ToList();
    }

    private void GetLocTireType(ref List<POWcsEquipLocHold> equipLocHold, List<PORcsLocation> locations, decimal outSide, string materialCode)
    {
        //判断大小胎
        _ = decimal.TryParse(_WmsSetting.GetWmsSettingByCode(ConstFields.MaterialOutSideType).Value, out var materialOutSide);
        //int tireType = outSide < materialOutSide ? (int)EnumTireType.Small : (int)EnumTireType.Big;
        //如果是NONE物料 都用小胎
        outSide = materialCode == ConstFields.NONE ? materialOutSide - 10 : outSide;

        equipLocHold = equipLocHold.Join(locations, f => f.LocId, s => s.Id, (f, s) => new
        {
            LocHold = f,
            Location = s,
        }).Where(t => outSide >= t.Location.LocMinOutsize && t.Location.LocMaxOutsize > outSide).Select(t => t.LocHold).ToList();

    }

    /// <summary>
    /// 任务数未饱和堆垛机V2
    /// </summary>
    /// <param name="pOWcsAsrsNetInputs"></param>
    /// <param name="targetTaskCounts"></param>
    private List<CrnTaskCount> GetStartLocTasksV2(ref List<POWcsAsrsNetInput> pOWcsAsrsNetInputs, List<POWcsAsrsCrnInput> wcsAsrsCrnInputs)
    {
        // 到堆垛机放货位 任务汇总数量 判断结束位用任务 判断开始位用指令
        // var taskIds = _RcsLocation.GetAllLocationsByType((int)EnumLocType.InLoc).Select(t => t.TaskId.GetValueOrDefault()).ToList();

        var taskIds = _RcsLocation.GetAllLocationTaskIdByType((int)EnumLocType.InLoc);
        var targetTaskCounts = new List<TaskGroupCount>();

        //如果是下传位置 则查找已执行的任务
        targetTaskCounts = _Task.GetTargetLocTaskGroup();

        // 放货位数量
        var startTaskCounts = _TaskCmd.GetStartLocTaskGroupContain(taskIds);

        // 堆垛机汇总数量
        var crnTaskCounts = new List<CrnTaskCount>();
        wcsAsrsCrnInputs.ForEach(item =>
        {
            var crnTask = crnTaskCounts.Where(t => t.CrnId == item.CrnId && t.AreaId == item.AreaId).FirstOrDefault();
            var targetLocCount = targetTaskCounts.Where(t => t.LocId == item.InputLoc1 || t.LocId == item.InputLoc2).Sum(t => t.Count);
            var startLoc = startTaskCounts.Where(t => t.LocId == item.InputLoc1 || t.LocId == item.InputLoc2).Sum(t => t.Count);

            //暂时给堆垛机数量固定2 保证不堵主线
            startLoc = startLoc == 0 ? startLoc : 2;
            if (crnTask is null)
            {
                crnTaskCounts.Add(new()
                {
                    CrnId = item.CrnId.GetValueOrDefault(),
                    AreaId = item.AreaId.GetValueOrDefault(),
                    Count = targetLocCount + startLoc,
                    MaxCount = item.TaskCount.GetValueOrDefault(),
                });
            }
            else
            {
                crnTask.Count = crnTask.Count + targetLocCount + startLoc;
            }
        });
        //var crnTaskCounts1 = crnTaskCounts.Where(t => t.MaxCount > t.Count).ToList();
        //if (crnTaskCounts1.Count <= 0)
        //{
        //    WriteLog($"出库缓存满,原可用出库路线数量为{crnTaskCounts.Count}");
        //}
        //WriteLog($"当前缓存{crnTaskCounts.ToJson()}", true);
        crnTaskCounts = crnTaskCounts.Where(t => t.MaxCount > t.Count).ToList();
        //WriteLog($"筛选后缓存{crnTaskCounts.ToJson()}", true);

        //WriteLog($"关联前路线{crnTaskCounts.ToJson()}", true);
        pOWcsAsrsNetInputs = pOWcsAsrsNetInputs.Join(crnTaskCounts, f => new { f.CrnId, f.AreaId }, s => new { s.CrnId, s.AreaId }, (f, s) => f).ToList();
        //WriteLog($"关联后路线{crnTaskCounts.ToJson()}", true);
        return crnTaskCounts;
    }


    private List<CrnTaskCount> GetStartLocTasksV3(ref List<POWcsAsrsNetInput> pOWcsAsrsNetInputs, List<POWcsAsrsCrnInput> wcsAsrsCrnInputs, decimal height, bool IsExecute)
    {
        // 到堆垛机放货位 任务汇总数量 判断结束位用任务 判断开始位用指令
        //var taskIds = _RcsLocation.GetAllLocationsByType((int)EnumLocType.InLoc).Select(t => t.TaskId.GetValueOrDefault()).ToList();
        var taskIds = _RcsLocation.GetAllLocationTaskIdByType((int)EnumLocType.InLoc);
        var targetTaskCounts = new List<TaskGroupCount>();

        //如果是下传位置 则查找已执行的任务
        targetTaskCounts = _Task.GetTargetLocTaskGroup();

        // 放货位数量
        var startTaskCounts = _TaskCmd.GetStartLocTaskGroupContain(taskIds);

        // 堆垛机汇总数量
        var crnTaskCounts = new List<CrnTaskCount>();
        wcsAsrsCrnInputs.ForEach(item =>
        {
            var crnTask = crnTaskCounts.Where(t => t.CrnId == item.CrnId && t.AreaId == item.AreaId).FirstOrDefault();
            var targetLocCount = targetTaskCounts.Where(t => t.LocId == item.InputLoc1 || t.LocId == item.InputLoc2).Sum(t => t.Count);
            var startLoc = startTaskCounts.Where(t => t.LocId == item.InputLoc1 || t.LocId == item.InputLoc2).Sum(t => t.Count);

            //暂时给堆垛机数量固定2 保证不堵主线
            startLoc = startLoc == 0 ? startLoc : 2;
            if (crnTask is null)
            {
                crnTaskCounts.Add(new()
                {
                    CrnId = item.CrnId.GetValueOrDefault(),
                    AreaId = item.AreaId.GetValueOrDefault(),
                    Count = targetLocCount + startLoc,
                    MaxCount = item.TaskCount.GetValueOrDefault(),
                });
            }
            else
            {
                crnTask.Count = crnTask.Count + targetLocCount + startLoc;
            }
        });
        //var crnTaskCounts1 = crnTaskCounts.Where(t => t.MaxCount > t.Count).ToList();
        //if (crnTaskCounts1.Count <= 0)
        //{
        //    WriteLog($"出库缓存满,原可用出库路线数量为{crnTaskCounts.Count}");
        //}

        // crnTaskCounts = crnTaskCounts.Where(t => t.MaxCount > t.Count).ToList();
        // crnTaskCounts = crnTaskCountsA.Union(crnTaskCountsB).ToList();

        var wmsMaterialHeight = _WmsSetting.GetWmsSettingByCode(ConstFields.MaterialHeightSideType);
        _ = int.TryParse(wmsMaterialHeight?.Value, out var settingHeight);


        //如果是高胎(含设置的最小值) 走原来逻辑
        if (height >= settingHeight)
        {
            crnTaskCounts = crnTaskCounts.Where(t => t.MaxCount > t.Count).ToList();

        }
        else//走缓存逻辑
        {

            //正常判断A库缓存
            var crnTaskCountsA = crnTaskCounts.Where(t => t.MaxCount > t.Count && t.AreaId == 10020101).ToList();
            //判断B C库缓存
            var crnTaskCountsB = crnTaskCounts.Where(t => t.AreaId != 10020101).ToList();
            //B C实际任务数
            var allCount = crnTaskCountsB.Select(t => t.Count).Sum();
            //系统设置最大缓存数
            _ = decimal.TryParse(_WmsSetting.GetWmsSettingByCode(ConstFields.WarehouseCacheNum).Value, out var warehouseCacheNum);
            //如果设置的最大数量大于实际数量 把BC缓存也加上
            crnTaskCounts = crnTaskCountsA;
            if (warehouseCacheNum > allCount)
            {
                crnTaskCounts.AddRange(crnTaskCountsB);
            }
        }
        pOWcsAsrsNetInputs = pOWcsAsrsNetInputs.Join(crnTaskCounts, f => new { f.CrnId, f.AreaId }, s => new { s.CrnId, s.AreaId }, (f, s) => f).ToList();

        return crnTaskCounts;
    }

    /// <summary>
    /// 任务数未饱和堆垛机
    /// </summary>
    /// <param name="crnId"></param>
    /// <param name="areaId"></param>
    public bool IsCrnTaskCounts(long crnId, long areaId)
    {
        // 到堆垛机放货位 任务汇总数量 判断结束位用任务 判断开始位用指令
        //var taskIds = _RcsLocation.GetAllLocationsByType((int)EnumLocType.InLoc)
        //    .Select(t => t.TaskId.GetValueOrDefault()).ToList();

        var taskIds = _RcsLocation.GetAllLocationTaskIdByType((int)EnumLocType.InLoc);

        var targetTaskCounts = new List<TaskGroupCount>();

        targetTaskCounts = _Task.GetTargetLocTaskGroup();
        // 开始位汇总
        var startTaskCounts = _TaskCmd.GetStartLocTaskGroupContain(taskIds);

        // 支线任务限制
        var wcsAsrsCrnInputs = _WcsAsrsCrnInput.GetWcsAsrsCrnInputs().Where(t => t.CrnId == crnId && t.AreaId == areaId).ToList();

        // 堆垛机汇总数量
        var crnTaskCounts = new List<CrnTaskCount>();
        wcsAsrsCrnInputs.ForEach(item =>
        {
            var crnTask = crnTaskCounts.Where(t => t.CrnId == item.CrnId && t.AreaId == item.AreaId).FirstOrDefault();
            var targetLocCount = targetTaskCounts.Where(t => t.LocId == item.InputLoc1 || t.LocId == item.InputLoc2).Sum(t => t.Count);
            var startLoc = startTaskCounts.Where(t => t.LocId == item.InputLoc1 || t.LocId == item.InputLoc2).Sum(t => t.Count);
            //暂时给堆垛机数量固定2 保证不堵主线
            startLoc = startLoc == 0 ? startLoc : 2;
            if (crnTask is null)
            {
                crnTaskCounts.Add(new()
                {
                    CrnId = item.CrnId.GetValueOrDefault(),
                    AreaId = item.AreaId.GetValueOrDefault(),
                    Count = targetLocCount + startLoc,
                    MaxCount = item.TaskCount.GetValueOrDefault(),
                });
            }
            else
            {
                crnTask.Count = crnTask.Count + targetLocCount + startLoc;
            }
        });

        return crnTaskCounts.Where(t => t.MaxCount > t.Count).Any();
    }

    /// <summary>
    /// 所有堆垛机任务数统一限制
    /// </summary>
    /// <param name="pOWcsAsrsNetInputs"></param>
    /// <param name="targetTaskCounts"></param>
    private void GetStartLocTasks(ref List<POWcsAsrsNetInput> pOWcsAsrsNetInputs)
    {
        // 到堆垛机放货位 任务汇总数量
        var targetTaskCounts = _Task.GetTargetLocTaskGroup();

        // (个)入库站台位任务限制
        var wmsSetting = _WmsSetting.GetWmsSettingByCode(ConstFields.WarehousingTask);
        _ = int.TryParse(wmsSetting?.Value, out var number);

        var inputs = pOWcsAsrsNetInputs.GroupJoin(targetTaskCounts,
            f => f.TargetLoc,
            s => s.LocId, (f, s) => new
            {
                Asrs = f,
                Task = s
            }).SelectMany(x => x.Task.DefaultIfEmpty(),
            (x, y) => new { x.Asrs, Task = y, Count = number - (y?.Count ?? 0) })
        .ToList();

        // 任务数不满堆垛机
        pOWcsAsrsNetInputs = inputs
            .Where(t => t.Count > 0)
            .Select(t => t.Asrs).ToList();

        if (pOWcsAsrsNetInputs.Count == 0)
        {
            // 无满足堆垛机 选择全部
            pOWcsAsrsNetInputs = inputs.Select(t => t.Asrs).ToList();
        }

    }

    /// <summary>
    /// 库位对应堆垛机物料汇总
    /// </summary>
    /// <param name="inStock"></param>
    /// <param name="equipLocHold"></param>
    /// <returns></returns>
    private List<CrnMaterialCount> GetCrnMaterialCount(long materialId)
    {
        // 所有堆垛机管理的货位
        var equipLocHold = _WcsEquipLocHold.GetEquipLocHolds();

        // 出库有货货位
        var inStock = _RcsLocation.GetInStockLocations(EnumInStockType.ExWarehouse);

        // 汇总堆垛机物料 数量
        var materialCounts = inStock.Join(equipLocHold,
            f => f.Id,
            s => s.LocId,
            (f, s) => new { CrnId = s.EquipId, Material = f.MaterialId })
            .Where(t => t.Material == materialId) // 物料筛选
            .GroupBy(t => new { t.CrnId, t.Material })
            .Select(s => new CrnMaterialCount
            {
                CrnId = s.Key.CrnId,
                MaterialId = s.Key.Material,
                Count = s.Count()
            }).ToList();

        var ids = materialCounts.Select(t => t.CrnId).ToList();
        equipLocHold = equipLocHold.Where(t => !ids.Contains(t.EquipId)).ToList();

        // 添加无此物料
        equipLocHold.Select(t => t.EquipId).Distinct().ToList()
            .ForEach(item =>
            {
                var mater = new CrnMaterialCount { CrnId = item, MaterialId = materialId, Count = 0 };
                materialCounts.Add(mater);
            });
        return materialCounts;
    }

    /// <summary>
    /// 物料均衡
    /// </summary>
    /// <param name="crnMaterialCounts">立库有货物料汇总数量</param>
    /// <param name="pOWcsAsrsNetInputs">可以到达的堆垛机放货位</param>
    /// <param name="pOWcsEquipLocHolds">堆垛机管理货位</param>
#pragma warning disable IDE0051 // 删除未使用的私有成员
    private void GetCrnMaterialTask(ref List<CrnMaterialCount> crnMaterialCounts,
#pragma warning restore IDE0051 // 删除未使用的私有成员
            List<POWcsAsrsNetInput> pOWcsAsrsNetInputs,
        List<POWcsEquipLocHold> pOWcsEquipLocHolds)
    {
        var materialTasks = _Task.GetMaterialTaskGroup();

        // 到堆垛机目标放货位
        var taskCount = pOWcsAsrsNetInputs
            .Join(materialTasks,
            f => f.TargetLoc,
            s => s.LocId,
            (f, s) => new { CrnId = f.CrnId, Count = s.Count })
            .ToList();

        // 到具体立库货位
        var equipTaskCount = pOWcsEquipLocHolds.Join(materialTasks,
            f => f.LocId,
            s => s.LocId,
            (f, s) => new { CrnId = f.EquipId, Count = s.Count })
            .ToList();

        crnMaterialCounts.ForEach(item =>
        {
            var m0 = taskCount.Where(t => t.CrnId == item.CrnId).FirstOrDefault();
            var m1 = equipTaskCount.Where(t => t.CrnId == item.CrnId).FirstOrDefault();
            if (m0 != null)
            {
                item.Count += m0.Count;
            }
            if (m1 != null)
            {
                item.Count += m1.Count;
            }
        });

        var ids = pOWcsAsrsNetInputs.Select(t => t.CrnId).ToList();
        crnMaterialCounts = crnMaterialCounts.Where(t => ids.Contains(t.CrnId)).ToList();
    }

    public int GetAsrsNetInputByInputLoc(long loc, long materialId)
    {

        var db = Db.Default;
        var crnInput = db.WcsAsrsCrnInputs.Where(t => t.InputLoc1 == loc).FirstOrDefault();

        var crnId = crnInput?.CrnId;

        var locs = _WcsEquipLocHold.GetEquipLocHolds().Where(t => t.EquipId == crnId).Select(t => t.LocId).ToList();

        var count = _RcsLocation.GetLocationsByLocIds(locs).Where(t => t.MaterialId == materialId).Count();

        return count;
    }

    public bool IfStartLocToTargetLoc(long startLoc, long targetLoc)
    {
        bool result = false;
        var db = Db.Default;
        result = db.WcsAsrsNetInputs
            .Where(t => t.StartLoc == startLoc && t.UseFlag == 1
            && t.TargetLoc == targetLoc).Any();
        return result;
    }

}


internal class CrnCount
{
    public long? CrnId { get; set; }
    public int Count { get; set; }
}
internal class CrnMaterialCount
{
    public long? CrnId { get; set; }
    public int Count { get; set; }
    public long? MaterialId { get; set; }
}